package it.tukano.treditor.modules.datamodel;

import com.sun.tracing.dtrace.ArgsAttributes;
import java.util.Arrays;

/**
 * Event sent by the data model (as a reaction to a notify request). Immutable.
 * @author pgi
 */
public class DataModelEvent {
    
    private final Object eventSource;
    private final Object[] arguments;
    private final DataModelEvent.EventType type;

    /**
     * Initializes this event
     * @param eventSource the source of the event (not necessarily the model)
     * @param type the type of the event
     * @param arguments the arguments of the event. Can be null. 
     */
    public DataModelEvent(Object eventSource, EventType type, Object... arguments) {
        this.eventSource = eventSource;
        this.arguments = arguments == null ? null : Arrays.copyOf(arguments, arguments.length);
        this.type = type;
    }
    
    /**
     * Take an argument from  the argument list of this event.
     * @param <T> the type of the argument
     * @param index the index of the argument in the argument list
     * @param type the class of the argument
     * @return the requested argument. Throws a lot of exceptions if the
     * index is out of range or the actual type is not of type K <: T
     */
    public <T> T getArg(int index, Class<T> type) {
        return type.cast(arguments[index]);
    }
    
    public <T> T getArgOrElse(int index, Class<T> type, T defaultValue) {
        if(index >= getArgumentCount()) return defaultValue;
        Object arg = arguments[index];
        if(type.isAssignableFrom(arg.getClass())) return type.cast(arg);
        return defaultValue;
    }

    /**
     * Returns the source of the event. This can be used to decide if the event
     * is fired back to the notifier (to avoid loop handling)
     * @return the source of the event.
     */
    public Object getEventSource() {
        return eventSource;
    }

    /**
     * Returns the type of the event
     * @return the type of the event
     */
    public EventType getType() {
        return type;
    }
    
    /**
     * Returns how many args this event has
     * @return the number of arguments of this event
     */
    public int getArgumentCount() {
        return arguments.length;
    }
    
    /**
     * Event qualifier
     */
    public static enum EventType {
        
        /**
         * The scene root has changed. Param: NodeElement (the new root)
         */
        SCENE_ROOT_SET,
        
        /**
         * The temporary elements root has changed. Param: NodeElement (the new temp root)
         */
        TEMP_ROOT_SET,
        
        /**
         * An empty node has been added to an existing node. Param: NodeElement (the parent), NodeElement (the empty child)
         */
        NODE_ADD_EMPTY_NODE,
        
        /**
         * A model has been added to an existing node. Param: NodeElement (the parent), ModelElement (the added model)
         */
        NODE_ADD_MODEL,
        
        /**
         * A mesh has been added to an existing node. Param: NodeElement (the parent), MeshElement (the added mesh)
         */
        NODE_ADD_MESH,
        
        /**
         * A directional light has been added to an existing node. Param: NodeElement (the parent), DirectionalLightElement (the added light)
         */
        NODE_ADD_DIRECTIONAL_LIGHT,
        
        /**
         * The selected element has changed. Param: SceneElement (the selected element): can be null. If null, the selection has been cleared.
         */
        SELECTED_ELEMENT_SET,
        
        /**
         * The current view has changed. Param: SceneView (the current view).
         */
        CURRENT_VIEW_SET,
        
        /**
         * The current model transform has been updated. Param: ModelElement (the updated model)
         */
        MODEL_TRANSFORM_UPDATE,
        
        /**
         * The director or color of a directional light has been updated. Param: DirectionalLight (the updated light)
         */
        DIRECTIONAL_LIGHT_UPDATE,
        
        /**
         * An element has been removed from a node. Param: SceneElement (the removed element), NodeElement (the node from which the element has been removed)
         */
        ELEMENT_REMOVED_FROM_NODE
    }

}
